home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / g_weapon.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-09-24  |  23.8 KB  |  921 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. #include "g_local.h"
  21.  
  22.  
  23. /*
  24. =================
  25. check_dodge
  26.  
  27. This is a support routine used when a client is firing
  28. a non-instant attack weapon.  It checks to see if a
  29. monster's dodge function should be called.
  30. =================
  31. */
  32. static void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed)
  33. {
  34.     vec3_t    end;
  35.     vec3_t    v;
  36.     trace_t    tr;
  37.     float    eta;
  38.  
  39.     // easy mode only ducks one quarter the time
  40.     if (skill->value == 0)
  41.     {
  42.         if (random() > 0.25)
  43.             return;
  44.     }
  45.     VectorMA (start, 8192, dir, end);
  46.     tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
  47.     if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) && infront(tr.ent, self))
  48.     {
  49.         VectorSubtract (tr.endpos, start, v);
  50.         eta = (VectorLength(v) - tr.ent->maxs[0]) / speed;
  51.         tr.ent->monsterinfo.dodge (tr.ent, self, eta);
  52.     }
  53. }
  54.  
  55.  
  56. /*
  57. =================
  58. fire_hit
  59.  
  60. Used for all impact (hit/punch/slash) attacks
  61. =================
  62. */
  63. qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick)
  64. {
  65.     trace_t        tr;
  66.     vec3_t        forward, right, up;
  67.     vec3_t        v;
  68.     vec3_t        point;
  69.     float        range;
  70.     vec3_t        dir;
  71.  
  72.     //see if enemy is in range
  73.     VectorSubtract (self->enemy->s.origin, self->s.origin, dir);
  74.     range = VectorLength(dir);
  75.     if (range > aim[0])
  76.         return false;
  77.  
  78.     if (aim[1] > self->mins[0] && aim[1] < self->maxs[0])
  79.     {
  80.         // the hit is straight on so back the range up to the edge of their bbox
  81.         range -= self->enemy->maxs[0];
  82.     }
  83.     else
  84.     {
  85.         // this is a side hit so adjust the "right" value out to the edge of their bbox
  86.         if (aim[1] < 0)
  87.             aim[1] = self->enemy->mins[0];
  88.         else
  89.             aim[1] = self->enemy->maxs[0];
  90.     }
  91.  
  92.     VectorMA (self->s.origin, range, dir, point);
  93.  
  94.     tr = gi.trace (self->s.origin, NULL, NULL, point, self, MASK_SHOT);
  95.     if (tr.fraction < 1)
  96.     {
  97.         if (!tr.ent->takedamage)
  98.             return false;
  99.         // if it will hit any client/monster then hit the one we wanted to hit
  100.         if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
  101.             tr.ent = self->enemy;
  102.     }
  103.  
  104.     AngleVectors(self->s.angles, forward, right, up);
  105.     VectorMA (self->s.origin, range, forward, point);
  106.     VectorMA (point, aim[1], right, point);
  107.     VectorMA (point, aim[2], up, point);
  108.     VectorSubtract (point, self->enemy->s.origin, dir);
  109.  
  110.     // do the damage
  111.     T_Damage (tr.ent, self, self, dir, point, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT);
  112.  
  113.     if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
  114.         return false;
  115.  
  116.     // do our special form of knockback here
  117.     VectorMA (self->enemy->absmin, 0.5, self->enemy->size, v);
  118.     VectorSubtract (v, point, v);
  119.     VectorNormalize (v);
  120.     VectorMA (self->enemy->velocity, kick, v, self->enemy->velocity);
  121.     if (self->enemy->velocity[2] > 0)
  122.         self->enemy->groundentity = NULL;
  123.     return true;
  124. }
  125.  
  126.  
  127. /*
  128. =================
  129. fire_lead
  130.  
  131. This is an internal support routine used for bullet/pellet based weapons.
  132. =================
  133. */
  134. static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod)
  135. {
  136.     trace_t        tr;
  137.     vec3_t        dir;
  138.     vec3_t        forward, right, up;
  139.     vec3_t        end;
  140.     float        r;
  141.     float        u;
  142.     vec3_t        water_start;
  143.     qboolean    water = false;
  144.     int            content_mask = MASK_SHOT | MASK_WATER;
  145.  
  146.     tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT);
  147.     if (!(tr.fraction < 1.0))
  148.     {
  149.         vectoangles (aimdir, dir);
  150.         AngleVectors (dir, forward, right, up);
  151.  
  152.         r = crandom()*hspread;
  153.         u = crandom()*vspread;
  154.         VectorMA (start, 8192, forward, end);
  155.         VectorMA (end, r, right, end);
  156.         VectorMA (end, u, up, end);
  157.  
  158.         if (gi.pointcontents (start) & MASK_WATER)
  159.         {
  160.             water = true;
  161.             VectorCopy (start, water_start);
  162.             content_mask &= ~MASK_WATER;
  163.         }
  164.  
  165.         tr = gi.trace (start, NULL, NULL, end, self, content_mask);
  166.  
  167.         // see if we hit water
  168.         if (tr.contents & MASK_WATER)
  169.         {
  170.             int        color;
  171.  
  172.             water = true;
  173.             VectorCopy (tr.endpos, water_start);
  174.  
  175.             if (!VectorCompare (start, tr.endpos))
  176.             {
  177.                 if (tr.contents & CONTENTS_WATER)
  178.                 {
  179.                     if (strcmp(tr.surface->name, "*brwater") == 0)
  180.                         color = SPLASH_BROWN_WATER;
  181.                     else
  182.                         color = SPLASH_BLUE_WATER;
  183.                 }
  184.                 else if (tr.contents & CONTENTS_SLIME)
  185.                     color = SPLASH_SLIME;
  186.                 else if (tr.contents & CONTENTS_LAVA)
  187.                     color = SPLASH_LAVA;
  188.                 else
  189.                     color = SPLASH_UNKNOWN;
  190.  
  191.                 if (color != SPLASH_UNKNOWN)
  192.                 {
  193.                     gi.WriteByte (svc_temp_entity);
  194.                     gi.WriteByte (TE_SPLASH);
  195.                     gi.WriteByte (8);
  196.                     gi.WritePosition (tr.endpos);
  197.                     gi.WriteDir (tr.plane.normal);
  198.                     gi.WriteByte (color);
  199.                     gi.multicast (tr.endpos, MULTICAST_PVS);
  200.                 }
  201.  
  202.                 // change bullet's course when it enters water
  203.                 VectorSubtract (end, start, dir);
  204.                 vectoangles (dir, dir);
  205.                 AngleVectors (dir, forward, right, up);
  206.                 r = crandom()*hspread*2;
  207.                 u = crandom()*vspread*2;
  208.                 VectorMA (water_start, 8192, forward, end);
  209.                 VectorMA (end, r, right, end);
  210.                 VectorMA (end, u, up, end);
  211.             }
  212.  
  213.             // re-trace ignoring water this time
  214.             tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT);
  215.         }
  216.     }
  217.  
  218.     // send gun puff / flash
  219.     if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
  220.     {
  221.         if (tr.fraction < 1.0)
  222.         {
  223.             if (tr.ent->takedamage)
  224.             {
  225.                 T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod);
  226.             }
  227.             else
  228.             {
  229.                 if (strncmp (tr.surface->name, "sky", 3) != 0)
  230.                 {
  231.                     gi.WriteByte (svc_temp_entity);
  232.                     gi.WriteByte (te_impact);
  233.                     gi.WritePosition (tr.endpos);
  234.                     gi.WriteDir (tr.plane.normal);
  235.                     gi.multicast (tr.endpos, MULTICAST_PVS);
  236.  
  237.                     if (self->client)
  238.                         PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
  239.                 }
  240.             }
  241.         }
  242.     }
  243.  
  244.     // if went through water, determine where the end and make a bubble trail
  245.     if (water)
  246.     {
  247.         vec3_t    pos;
  248.  
  249.         VectorSubtract (tr.endpos, water_start, dir);
  250.         VectorNormalize (dir);
  251.         VectorMA (tr.endpos, -2, dir, pos);
  252.         if (gi.pointcontents (pos) & MASK_WATER)
  253.             VectorCopy (pos, tr.endpos);
  254.         else
  255.             tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
  256.  
  257.         VectorAdd (water_start, tr.endpos, pos);
  258.         VectorScale (pos, 0.5, pos);
  259.  
  260.         gi.WriteByte (svc_temp_entity);
  261.         gi.WriteByte (TE_BUBBLETRAIL);
  262.         gi.WritePosition (water_start);
  263.         gi.WritePosition (tr.endpos);
  264.         gi.multicast (pos, MULTICAST_PVS);
  265.     }
  266. }
  267.  
  268.  
  269. /*
  270. =================
  271. fire_bullet
  272.  
  273. Fires a single round.  Used for machinegun and chaingun.  Would be fine for
  274. pistols, rifles, etc....
  275. =================
  276. */
  277. void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod)
  278. {
  279.     fire_lead (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod);
  280. }
  281.  
  282.  
  283. /*
  284. =================
  285. fire_shotgun
  286.  
  287. Shoots shotgun pellets.  Used by shotgun and super shotgun.
  288. =================
  289. */
  290. void fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod)
  291. {
  292.     int        i;
  293.  
  294.     for (i = 0; i < count; i++)
  295.         fire_lead (self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod);
  296. }
  297.  
  298.  
  299. /*
  300. =================
  301. fire_blaster
  302.  
  303. Fires a single blaster bolt.  Used by the blaster and hyper blaster.
  304. =================
  305. */
  306. void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  307. {
  308.     int        mod;
  309.  
  310.     if (other == self->owner)
  311.         return;
  312.  
  313.     if (surf && (surf->flags & SURF_SKY))
  314.     {
  315.         G_FreeEdict (self);
  316.         return;
  317.     }
  318.  
  319.     if (self->owner->client)
  320.         PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
  321.  
  322.     if (other->takedamage)
  323.     {
  324.         if (self->spawnflags & 1)
  325.             mod = MOD_HYPERBLASTER;
  326.         else
  327.             mod = MOD_BLASTER;
  328.         T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
  329.     }
  330.     else
  331.     {
  332.         gi.WriteByte (svc_temp_entity);
  333.         gi.WriteByte (TE_BLASTER);
  334.         gi.WritePosition (self->s.origin);
  335.         if (!plane)
  336.             gi.WriteDir (vec3_origin);
  337.         else
  338.             gi.WriteDir (plane->normal);
  339.         gi.multicast (self->s.origin, MULTICAST_PVS);
  340.     }
  341.  
  342.     G_FreeEdict (self);
  343. }
  344.  
  345. void fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper)
  346. {
  347.     edict_t    *bolt;
  348.     trace_t    tr;
  349.  
  350.     VectorNormalize (dir);
  351.  
  352.     bolt = G_Spawn();
  353.     VectorCopy (start, bolt->s.origin);
  354.     VectorCopy (start, bolt->s.old_origin);
  355.     vectoangles (dir, bolt->s.angles);
  356.     VectorScale (dir, speed, bolt->velocity);
  357.     bolt->movetype = MOVETYPE_FLYMISSILE;
  358.     bolt->clipmask = MASK_SHOT;
  359.     bolt->solid = SOLID_BBOX;
  360.     bolt->s.effects |= effect;
  361.     VectorClear (bolt->mins);
  362.     VectorClear (bolt->maxs);
  363.     bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2");
  364.     bolt->s.sound = gi.soundindex ("misc/lasfly.wav");
  365.     bolt->owner = self;
  366.     bolt->touch = blaster_touch;
  367.     bolt->nextthink = level.time + 2;
  368.     bolt->think = G_FreeEdict;
  369.     bolt->dmg = damage;
  370.     bolt->classname = "bolt";
  371.     if (hyper)
  372.         bolt->spawnflags = 1;
  373.     gi.linkentity (bolt);
  374.  
  375.     if (self->client)
  376.         check_dodge (self, bolt->s.origin, dir, speed);
  377.  
  378.     tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
  379.     if (tr.fraction < 1.0)
  380.     {
  381.         VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
  382.         bolt->touch (bolt, tr.ent, NULL, NULL);
  383.     }
  384. }    
  385.  
  386.  
  387. /*
  388. =================
  389. fire_grenade
  390. =================
  391. */
  392. static void Grenade_Explode (edict_t *ent)
  393. {
  394.     vec3_t        origin;
  395.     int            mod;
  396.  
  397.     if (ent->owner->client)
  398.         PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
  399.  
  400.     //FIXME: if we are onground then raise our Z just a bit since we are a point?
  401.     if (ent->enemy)
  402.     {
  403.         float    points;
  404.         vec3_t    v;
  405.         vec3_t    dir;
  406.  
  407.         VectorAdd (ent->enemy->mins, ent->enemy->maxs, v);
  408.         VectorMA (ent->enemy->s.origin, 0.5, v, v);
  409.         VectorSubtract (ent->s.origin, v, v);
  410.         points = ent->dmg - 0.5 * VectorLength (v);
  411.         VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir);
  412.         if (ent->spawnflags & 1)
  413.             mod = MOD_HANDGRENADE;
  414.         else
  415.             mod = MOD_GRENADE;
  416.         T_Damage (ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
  417.     }
  418.  
  419.     if (ent->spawnflags & 2)
  420.         mod = MOD_HELD_GRENADE;
  421.     else if (ent->spawnflags & 1)
  422.         mod = MOD_HG_SPLASH;
  423.     else
  424.         mod = MOD_G_SPLASH;
  425.     T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod);
  426.  
  427.     VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  428.     gi.WriteByte (svc_temp_entity);
  429.     if (ent->waterlevel)
  430.     {
  431.         if (ent->groundentity)
  432.             gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
  433.         else
  434.             gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
  435.     }
  436.     else
  437.     {
  438.         if (ent->groundentity)
  439.             gi.WriteByte (TE_GRENADE_EXPLOSION);
  440.         else
  441.             gi.WriteByte (TE_ROCKET_EXPLOSION);
  442.     }
  443.     gi.WritePosition (origin);
  444.     gi.multicast (ent->s.origin, MULTICAST_PHS);
  445.  
  446.     G_FreeEdict (ent);
  447. }
  448.  
  449. static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  450. {
  451.     if (other == ent->owner)
  452.         return;
  453.  
  454.     if (surf && (surf->flags & SURF_SKY))
  455.     {
  456.         G_FreeEdict (ent);
  457.         return;
  458.     }
  459.  
  460.     if (!other->takedamage)
  461.     {
  462.         if (ent->spawnflags & 1)
  463.         {
  464.             if (random() > 0.5)
  465.                 gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
  466.             else
  467.                 gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
  468.         }
  469.         else
  470.         {
  471.             gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
  472.         }
  473.         return;
  474.     }
  475.  
  476.     ent->enemy = other;
  477.     Grenade_Explode (ent);
  478. }
  479.  
  480. void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
  481. {
  482.     edict_t    *grenade;
  483.     vec3_t    dir;
  484.     vec3_t    forward, right, up;
  485.  
  486.     vectoangles (aimdir, dir);
  487.     AngleVectors (dir, forward, right, up);
  488.  
  489.     grenade = G_Spawn();
  490.     VectorCopy (start, grenade->s.origin);
  491.     VectorScale (aimdir, speed, grenade->velocity);
  492.     VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
  493.     VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
  494.     VectorSet (grenade->avelocity, 300, 300, 300);
  495.     grenade->movetype = MOVETYPE_BOUNCE;
  496.     grenade->clipmask = MASK_SHOT;
  497.     grenade->solid = SOLID_BBOX;
  498.     grenade->s.effects |= EF_GRENADE;
  499.     VectorClear (grenade->mins);
  500.     VectorClear (grenade->maxs);
  501.     grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2");
  502.     grenade->owner = self;
  503.     grenade->touch = Grenade_Touch;
  504.     grenade->nextthink = level.time + timer;
  505.     grenade->think = Grenade_Explode;
  506.     grenade->dmg = damage;
  507.     grenade->dmg_radius = damage_radius;
  508.     grenade->classname = "grenade";
  509.  
  510.     gi.linkentity (grenade);
  511. }
  512.  
  513. void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held)
  514. {
  515.     edict_t    *grenade;
  516.     vec3_t    dir;
  517.     vec3_t    forward, right, up;
  518.  
  519.     vectoangles (aimdir, dir);
  520.     AngleVectors (dir, forward, right, up);
  521.  
  522.     grenade = G_Spawn();
  523.     VectorCopy (start, grenade->s.origin);
  524.     VectorScale (aimdir, speed, grenade->velocity);
  525.     VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
  526.     VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
  527.     VectorSet (grenade->avelocity, 300, 300, 300);
  528.     grenade->movetype = MOVETYPE_BOUNCE;
  529.     grenade->clipmask = MASK_SHOT;
  530.     grenade->solid = SOLID_BBOX;
  531.     grenade->s.effects |= EF_GRENADE;
  532.     VectorClear (grenade->mins);
  533.     VectorClear (grenade->maxs);
  534.     grenade->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2");
  535.     grenade->owner = self;
  536.     grenade->touch = Grenade_Touch;
  537.     grenade->nextthink = level.time + timer;
  538.     grenade->think = Grenade_Explode;
  539.     grenade->dmg = damage;
  540.     grenade->dmg_radius = damage_radius;
  541.     grenade->classname = "hgrenade";
  542.     if (held)
  543.         grenade->spawnflags = 3;
  544.     else
  545.         grenade->spawnflags = 1;
  546.     grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav");
  547.  
  548.     if (timer <= 0.0)
  549.         Grenade_Explode (grenade);
  550.     else
  551.     {
  552.         gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0);
  553.         gi.linkentity (grenade);
  554.     }
  555. }
  556.  
  557.  
  558. /*
  559. =================
  560. fire_rocket
  561. =================
  562. */
  563. void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
  564. {
  565.     vec3_t        origin;
  566.     int            n;
  567.  
  568.     if (other == ent->owner)
  569.         return;
  570.  
  571.     if (surf && (surf->flags & SURF_SKY))
  572.     {
  573.         G_FreeEdict (ent);
  574.         return;
  575.     }
  576.  
  577.     if (ent->owner->client)
  578.         PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
  579.  
  580.     // calculate position for the explosion entity
  581.     VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
  582.  
  583.     if (other->takedamage)
  584.     {
  585.         T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET);
  586.     }
  587.     else
  588.     {
  589.         // don't throw any debris in net games
  590.         if (!deathmatch->value && !coop->value)
  591.         {
  592.             if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING)))
  593.             {
  594.                 n = rand() % 5;
  595.                 while(n--)
  596.                     ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin);
  597.             }
  598.         }
  599.     }
  600.  
  601.     T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);
  602.  
  603.     gi.WriteByte (svc_temp_entity);
  604.     if (ent->waterlevel)
  605.         gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
  606.     else
  607.         gi.WriteByte (TE_ROCKET_EXPLOSION);
  608.     gi.WritePosition (origin);
  609.     gi.multicast (ent->s.origin, MULTICAST_PHS);
  610.  
  611.     G_FreeEdict (ent);
  612. }
  613.  
  614. void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
  615. {
  616.     edict_t    *rocket;
  617.  
  618.     rocket = G_Spawn();
  619.     VectorCopy (start, rocket->s.origin);
  620.     VectorCopy (dir, rocket->movedir);
  621.     vectoangles (dir, rocket->s.angles);
  622.     VectorScale (dir, speed, rocket->velocity);
  623.     rocket->movetype = MOVETYPE_FLYMISSILE;
  624.     rocket->clipmask = MASK_SHOT;
  625.     rocket->solid = SOLID_BBOX;
  626.     rocket->s.effects |= EF_ROCKET;
  627.     VectorClear (rocket->mins);
  628.     VectorClear (rocket->maxs);
  629.     rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2");
  630.     rocket->owner = self;
  631.     rocket->touch = rocket_touch;
  632.     rocket->nextthink = level.time + 8000/speed;
  633.     rocket->think = G_FreeEdict;
  634.     rocket->dmg = damage;
  635.     rocket->radius_dmg = radius_damage;
  636.     rocket->dmg_radius = damage_radius;
  637.     rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
  638.     rocket->classname = "rocket";
  639.  
  640.     if (self->client)
  641.         check_dodge (self, rocket->s.origin, dir, speed);
  642.  
  643.     gi.linkentity (rocket);
  644. }
  645.  
  646.  
  647. /*
  648. =================
  649. fire_rail
  650. =================
  651. */
  652. void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick)
  653. {
  654.     vec3_t        from;
  655.     vec3_t        end;
  656.     trace_t        tr;
  657.     edict_t        *ignore;
  658.     int            mask;
  659.     qboolean    water;
  660.  
  661.     VectorMA (start, 8192, aimdir, end);
  662.     VectorCopy (start, from);
  663.     ignore = self;
  664.     water = false;
  665.     mask = MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA;
  666.     while (ignore)
  667.     {
  668.         tr = gi.trace (from, NULL, NULL, end, ignore, mask);
  669.  
  670.         if (tr.contents & (CONTENTS_SLIME|CONTENTS_LAVA))
  671.         {
  672.             mask &= ~(CONTENTS_SLIME|CONTENTS_LAVA);
  673.             water = true;
  674.         }
  675.         else
  676.         {
  677.             //ZOID--added so rail goes through SOLID_BBOX entities (gibs, etc)
  678.             if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client) ||
  679.                 (tr.ent->solid == SOLID_BBOX))
  680.                 ignore = tr.ent;
  681.             else
  682.                 ignore = NULL;
  683.  
  684.             if ((tr.ent != self) && (tr.ent->takedamage))
  685.                 T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN);
  686.         }
  687.  
  688.         VectorCopy (tr.endpos, from);
  689.     }
  690.  
  691.     // send gun puff / flash
  692.     gi.WriteByte (svc_temp_entity);
  693.     gi.WriteByte (TE_RAILTRAIL);
  694.     gi.WritePosition (start);
  695.     gi.WritePosition (tr.endpos);
  696.     gi.multicast (self->s.origin, MULTICAST_PHS);
  697. //    gi.multicast (start, MULTICAST_PHS);
  698.     if (water)
  699.     {
  700.         gi.WriteByte (svc_temp_entity);
  701.         gi.WriteByte (TE_RAILTRAIL);
  702.         gi.WritePosition (start);
  703.         gi.WritePosition (tr.endpos);
  704.         gi.multicast (tr.endpos, MULTICAST_PHS);
  705.     }
  706.  
  707.     if (self->client)
  708.         PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
  709. }
  710.  
  711.  
  712. /*
  713. =================
  714. fire_bfg
  715. =================
  716. */
  717. void bfg_explode (edict_t *self)
  718. {
  719.     edict_t    *ent;
  720.     float    points;
  721.     vec3_t    v;
  722.     float    dist;
  723.  
  724.     if (self->s.frame == 0)
  725.     {
  726.         // the BFG effect
  727.         ent = NULL;
  728.         while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL)
  729.         {
  730.             if (!ent->takedamage)
  731.                 continue;
  732.             if (ent == self->owner)
  733.                 continue;
  734.             if (!CanDamage (ent, self))
  735.                 continue;
  736.             if (!CanDamage (ent, self->owner))
  737.                 continue;
  738.  
  739.             VectorAdd (ent->mins, ent->maxs, v);
  740.             VectorMA (ent->s.origin, 0.5, v, v);
  741.             VectorSubtract (self->s.origin, v, v);
  742.             dist = VectorLength(v);
  743.             points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius));
  744.             if (ent == self->owner)
  745.                 points = points * 0.5;
  746.  
  747.             gi.WriteByte (svc_temp_entity);
  748.             gi.WriteByte (TE_BFG_EXPLOSION);
  749.             gi.WritePosition (ent->s.origin);
  750.             gi.multicast (ent->s.origin, MULTICAST_PHS);
  751.             T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT);
  752.         }
  753.     }
  754.  
  755.     self->nextthink = level.time + FRAMETIME;
  756.     self->s.frame++;
  757.     if (self->s.frame == 5)
  758.         self->think = G_FreeEdict;
  759. }
  760.  
  761. void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
  762. {
  763.     if (other == self->owner)
  764.         return;
  765.  
  766.     if (surf && (surf->flags & SURF_SKY))
  767.     {
  768.         G_FreeEdict (self);
  769.         return;
  770.     }
  771.  
  772.     if (self->owner->client)
  773.         PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
  774.  
  775.     // core explosion - prevents firing it into the wall/floor
  776.     if (other->takedamage)
  777.         T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST);
  778.     T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST);
  779.  
  780.     gi.sound (self, CHAN_VOICE, gi.soundindex ("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0);
  781.     self->solid = SOLID_NOT;
  782.     self->touch = NULL;
  783.     VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin);
  784.     VectorClear (self->velocity);
  785.     self->s.modelindex = gi.modelindex ("sprites/s_bfg3.sp2");
  786.     self->s.frame = 0;
  787.     self->s.sound = 0;
  788.     self->s.effects &= ~EF_ANIM_ALLFAST;
  789.     self->think = bfg_explode;
  790.     self->nextthink = level.time + FRAMETIME;
  791.     self->enemy = other;
  792.  
  793.     gi.WriteByte (svc_temp_entity);
  794.     gi.WriteByte (TE_BFG_BIGEXPLOSION);
  795.     gi.WritePosition (self->s.origin);
  796.     gi.multicast (self->s.origin, MULTICAST_PVS);
  797. }
  798.  
  799.  
  800. void bfg_think (edict_t *self)
  801. {
  802.     edict_t    *ent;
  803.     edict_t    *ignore;
  804.     vec3_t    point;
  805.     vec3_t    dir;
  806.     vec3_t    start;
  807.     vec3_t    end;
  808.     int        dmg;
  809.     trace_t    tr;
  810.  
  811.     if (deathmatch->value)
  812.         dmg = 5;
  813.     else
  814.         dmg = 10;
  815.  
  816.     ent = NULL;
  817.     while ((ent = findradius(ent, self->s.origin, 256)) != NULL)
  818.     {
  819.         if (ent == self)
  820.             continue;
  821.  
  822.         if (ent == self->owner)
  823.             continue;
  824.  
  825.         if (!ent->takedamage)
  826.             continue;
  827.  
  828.         if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0))
  829.             continue;
  830.  
  831. //ZOID
  832.         //don't target players in CTF
  833.         if (ctf->value && ent->client &&
  834.             self->owner->client &&
  835.             ent->client->resp.ctf_team == self->owner->client->resp.ctf_team)
  836.             continue;
  837. //ZOID
  838.  
  839.         VectorMA (ent->absmin, 0.5, ent->size, point);
  840.  
  841.         VectorSubtract (point, self->s.origin, dir);
  842.         VectorNormalize (dir);
  843.  
  844.         ignore = self;
  845.         VectorCopy (self->s.origin, start);
  846.         VectorMA (start, 2048, dir, end);
  847.         while(1)
  848.         {
  849.             tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
  850.  
  851.             if (!tr.ent)
  852.                 break;
  853.  
  854.             // hurt it if we can
  855.             if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner))
  856.                 T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);
  857.  
  858.             // if we hit something that's not a monster or player we're done
  859.             if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
  860.             {
  861.                 gi.WriteByte (svc_temp_entity);
  862.                 gi.WriteByte (TE_LASER_SPARKS);
  863.                 gi.WriteByte (4);
  864.                 gi.WritePosition (tr.endpos);
  865.                 gi.WriteDir (tr.plane.normal);
  866.                 gi.WriteByte (self->s.skinnum);
  867.                 gi.multicast (tr.endpos, MULTICAST_PVS);
  868.                 break;
  869.             }
  870.  
  871.             ignore = tr.ent;
  872.             VectorCopy (tr.endpos, start);
  873.         }
  874.  
  875.         gi.WriteByte (svc_temp_entity);
  876.         gi.WriteByte (TE_BFG_LASER);
  877.         gi.WritePosition (self->s.origin);
  878.         gi.WritePosition (tr.endpos);
  879.         gi.multicast (self->s.origin, MULTICAST_PHS);
  880.     }
  881.  
  882.     self->nextthink = level.time + FRAMETIME;
  883. }
  884.  
  885.  
  886. void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius)
  887. {
  888.     edict_t    *bfg;
  889.  
  890.     bfg = G_Spawn();
  891.     VectorCopy (start, bfg->s.origin);
  892.     VectorCopy (dir, bfg->movedir);
  893.     vectoangles (dir, bfg->s.angles);
  894.     VectorScale (dir, speed, bfg->velocity);
  895.     bfg->movetype = MOVETYPE_FLYMISSILE;
  896.     bfg->clipmask = MASK_SHOT;
  897.     bfg->solid = SOLID_BBOX;
  898.     bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST;
  899.     VectorClear (bfg->mins);
  900.     VectorClear (bfg->maxs);
  901.     bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2");
  902.     bfg->owner = self;
  903.     bfg->touch = bfg_touch;
  904.     bfg->nextthink = level.time + 8000/speed;
  905.     bfg->think = G_FreeEdict;
  906.     bfg->radius_dmg = damage;
  907.     bfg->dmg_radius = damage_radius;
  908.     bfg->classname = "bfg blast";
  909.     bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav");
  910.  
  911.     bfg->think = bfg_think;
  912.     bfg->nextthink = level.time + FRAMETIME;
  913.     bfg->teammaster = bfg;
  914.     bfg->teamchain = NULL;
  915.  
  916.     if (self->client)
  917.         check_dodge (self, bfg->s.origin, dir, speed);
  918.  
  919.     gi.linkentity (bfg);
  920. }
  921.